import ldap3 as ldap
from forum.actions.user import UserJoinsAction
from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext, InvalidAuthentication
from django.utils.translation import ugettext as _
from forum.models.user import User
from forum_modules.ldapauth.settings import LDAP_SERVER, LDAP_BASE_DN, LDAP_BIND_SECRET, LDAP_USE_TLS, LDAP_USER_MASK, \
    LDAP_DN, LDAP_NAME, LDAP_MAIL, LDAP_UID, LDAP_BIND_DN


class LDAPAuthConsumer(AuthenticationConsumer):

    def process_authentication_request(self, request):
        username = request.POST['username'].strip()
        password = request.POST['password']
        uid = str(LDAP_USER_MASK) % username

        # an empty password will cause ldap to try an anonymous bind. This is picked up here
        if not password:
            raise InvalidAuthentication(
                _('Login failed. Please enter valid username and password (both are case-sensitive)'))

        ldapo = ldap.initialize(str(LDAP_SERVER))
        ldapo = ldap.initialize(str(LDAP_SERVER))
        if (LDAP_USE_TLS):
            ldapo.start_tls_s()
        ldapo.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
        try:
            ldapo.simple_bind_s(str(LDAP_BIND_DN), str(LDAP_BIND_SECRET))
            search = ldapo.search_s(str(LDAP_BASE_DN), ldap.SCOPE_SUBTREE, uid)
        except ldap.LDAPError:
            # could not bind using credentials specified in ldap config
            raise InvalidAuthentication(_('Login failed - LDAP bind error. Please contact your system administrator'))

        ldapo.unbind_s()

        if not search:
            # could not find user
            raise InvalidAuthentication(
                _('Login failed. Please enter valid username and password (both are case-sensitive)'))

        # now try to bind as selected user; should raise exception if bind fails
        ldapo = ldap.initialize(str(LDAP_SERVER))
        if (LDAP_USE_TLS):
            ldapo.start_tls_s()
        ldapo.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
        try:
            ldapo.simple_bind_s(search[0][1][str(LDAP_DN)][0], password)
        except ldap.LDAPError:
            # could not bind as user - password is incorrect
            raise InvalidAuthentication(
                _('Login failed. Please enter valid username and password (both are case-sensitive)'))
        ldapo.unbind_s()

        try:
            return User.objects.get(username=username)
        except User.DoesNotExist:
            userinfo = search[0][1]
            _user = User(username=userinfo[str(LDAP_UID)][0],
                         email=userinfo[str(LDAP_MAIL)][0],
                         real_name=userinfo[str(LDAP_NAME)][0])
            _user.email_isvalid = True
            _user.set_unusable_password()
            _user.save()
            UserJoinsAction(user=_user, ip=request.META['REMOTE_ADDR']).save()
            return _user


class LDAPAuthContext(ConsumerTemplateContext):
    mode = 'STACK_ITEM'
    weight = 1000
    human_name = 'LDAP authentication'
    stack_item_template = 'modules/ldapauth/loginform.html'
    show_to_logged_in_user = False
